import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:share_fit/app/controllers/base/base_controller.dart';
import 'package:share_fit/app/controllers/home/cycle_calendar/cycle_calendar_setting/logic.dart';
import 'package:share_fit/app/controllers/home/home_const.dart';
import 'package:share_fit/app/models/home/menstrual_mode.dart';
import 'package:share_fit/app/routes/home_routes.dart';
import 'package:share_fit/app/routes/routes.dart';
import 'package:share_fit/app/tools/date_tool.dart';
import 'package:share_fit/app/ute/uteped_const.dart';
import 'package:table_calendar/table_calendar.dart';
import 'state.dart';

class CycleCalendarLogic extends BaseController {
  final CycleCalendarState state = CycleCalendarState();
  final settingLogic = Get.put(CycleCalendarSettingLogic());

  goBack() {
    Future.delayed(Duration.zero, () => Get.until((route) => route.settings.name == Routes.mainPage));
  }

  onPopInvoked(bool didPop) {
    if (didPop) return;
    goBack();
  }

  goSettingPage() {
    Get.toNamed(HomeRoutes.cycleCalendarSettingPage);
  }

  changeFocusDay(DateTime focusedDay) {
    state.focusedDay = focusedDay;
    update(['cycle_calendar_events']);
  }

  onDaySelected(DateTime? selectedDay, DateTime focusedDay) {
    if (!isSameDay(state.selectedDay, selectedDay)) {
      state.selectedDay = selectedDay;
      state.focusedDay = focusedDay;
      update(['cycle_calendar_events']);
    }
  }

  onRangeSelected(DateTime? start, DateTime? end, DateTime focusedDay) {
    state.selectedDay = null;
    state.focusedDay = focusedDay;
    state.rangeStart = start;
    state.rangeEnd = end;
    update(['cycle_calendar_events']);
  }

  List<MenstrualEvent> getEventsForDay(DateTime day) {
    if (getAddress().isEmpty) return [];

    Map<DateTime, List<MenstrualEvent>> eventSource = _getMenstrualPredictionData();

    int getHashCode(DateTime key) {
      return key.day * 1000000 + key.month * 10000 + key.year;
    }

    Map<DateTime, List<MenstrualEvent>> events = LinkedHashMap<DateTime, List<MenstrualEvent>>(
      equals: isSameDay,
      hashCode: getHashCode,
    )..addAll(eventSource);

    return events[day] ?? [];
  }

  Map<DateTime, List<MenstrualEvent>> _getMenstrualPredictionData() {
    int? dt = getMenstruationStartDate();
    if (dt != null) {
      SplayTreeMap<DateTime, List<MenstrualEvent>> events = SplayTreeMap();

      /// 经期开始日
      DateTime startDate = DateTime.fromMillisecondsSinceEpoch(dt);
      DateTime today = DateTool.truncateHMS();

      for (int i = 0; i < 6; i++) {
        MenstrualModel model = settingLogic.getMenstrualData(startDate: startDate);
        events.addAll(model.toOvulationEvent());

        if (DateTool.differentInDays(startDate, today) < 0) {
          // 将之前的的经期和正在经期的日期标红
          if (model.inMenstrualRange(startDate) || model.inPredictionRange(startDate)) {
            events.addAll(model.toMenstruatingEvent());
          }
        } else if (DateTool.differentInDays(startDate, today) == 0) {
          events.addAll(model.toMenstruatingEvent());
        } else {
          events.addAll(model.toMenstruatingEvent(dotColor: 0xFFFFD740));
        }
        startDate = DateTime.fromMillisecondsSinceEpoch(model.predictionStart!);
      }

      state.lastDay = events.entries.last.key;
      return events;
    } else {
      return {};
    }
  }

  /// 从设置页获取经期开始日期
  DateTime? getMenstruationStartFromSettingPage() {
    int startTime = read(HomeConst.cycleCalendarSettingStartTime, defaultValue: 0);
    return startTime == 0 ? null : DateTime.fromMillisecondsSinceEpoch(startTime);
  }

  /// 1、如果设置页设置的开始时间和结束时间，就将设置页的开始时间作为经期的开始日期
  /// 2、果设置页未设置的开始时间和结束时间，就将日历上选中的日期作为经期的开始日期
  int? getMenstruationStartDate() {
    DateTime? menstruationDate = getMenstruationStartFromSettingPage();
    return menstruationDate != null ? getMenstruationStartFromSettingPage()?.millisecondsSinceEpoch : null;
  }

  @override
  String getRemotePath() {
    return UTEPedConst.pathMenstrual;
  }

  @override
  String getStoragePath() {
    return UTEPedConst.pathMenstrual;
  }

  @override
  void initState() {
    state.selectedDay = DateTime.now();
    DateTime ld = DateTime.fromMillisecondsSinceEpoch(getMenstruationStartDate() ?? DateTime.now().millisecondsSinceEpoch);
    state.lastDay = DateUtils.addMonthsToMonthDate(ld, 3);
  }

  @override
  void onRemoteEvent(event, method) {
    if (method == UTEPedConst.methodSetPhysiologicalTime) {
      _setPhysiologicalTime();
    } else if (method == UTEPedConst.methodSetCycle) {
      settingLogic.syncMenstrualCycleData();
    }
  }

  void _setPhysiologicalTime() {
    bRemoteReqAsync(UTEPedConst.methodSetPhysiologicalTime, {UTEPedConst.keyValue: DateTool.getTimeInSeconds(DateTime.now(), truncateHms: false)});
  }
}
